#include <iostream>
#include "parser.hpp"

#include "expression.hpp"
#include "const_expr.hpp"
#include "assign_stat.hpp"
#include "typedef_stat.hpp"
#include "types.hpp"

const Token END_OF_FILE_TOK = Token(END_OF_FILE);

class Parser {
public:
    Parser(const std::vector<Token>& tokens) : tokens(tokens) {
        this->length = tokens.size();
        this->current_pos = 0;
    }

    std::vector<Statement*> parse() {
        while (!(match(END_OF_FILE))) {
            output.push_back(find_statement());
        }
        return output;
    }
private:
    Statement* find_statement() {
        if (match(WORD)) {
            if (match(WORD)) {
                if (match(EQU)) {
                    return new AssignmentStatement(peek(-3).get_value(), peek(-2).get_value(), find_expression());
                }
            }
        }

        if (match(TYPEDEF)) {
            if (match(WORD)) {
                if (match(WORD)) {
                    return new TypedefStatement(peek(-2).get_value(), peek(-1).get_value());
                }
            }
        }

        throw std::runtime_error("Error in find_statement()");
    }

    Expression* find_expression() {
        return primary();
    }

    Expression* primary() {
        Token current = peek();

        if (match(NUMBER)) {
            return new ConstExpression(std::stoi(current.get_value()));
        }

        throw std::runtime_error("Error in primary()");
    }

    bool match(TokenType type) {
        if (!look_match(type)) return false;
        current_pos++;
        return true;
    }

    bool look_match(TokenType type) {
        return look_match(0, type);
    }

    bool look_match(int pos, TokenType type) {
        return type == peek(pos).get_type();
    }

    Token peek() {
        return peek(0);
    }

    Token peek(int pos) {
        int new_pos = current_pos + pos;
        if (new_pos >= length) return END_OF_FILE_TOK;
        return tokens[new_pos];
    }

    const std::vector<Token>& tokens;
    int length;
    int current_pos;
    std::vector<Statement*> output;
};


std::vector<Statement*> parse(const std::vector<Token>& tokens) {
    return Parser(tokens).parse();
}
